package redis

import (
	"fmt"
	"github.com/go-redis/redis/v8"
	"go.uber.org/zap"
	"time"
)

// 声明封装 redis 所有操作方法
type IRedisHandler interface {
	// ------------------------ 数据键值相关操作
	// 返回 key 所储存的值的类型，如果 key 不存在，则返回 none
	Type(key string) (valueType string, err error)
	// 设置 key 在 millisecond 毫秒后过期，key 过期后将不再可用
	PExpire(key string, millisecond int64) (status bool, err error)
	// 以 UNIX 时间戳设置 key 在指定具体时间过期，key 过期后将不再可用
	PExpireAt(key string, timestamp int64) (status bool, err error)
	// 修改 key 的名称，成功返回 OK ，失败时候返回一个错误
	Rename(key string, newKey string) (status string, err error)
	// 移除给定 key 的过期时间，使得 key 永不过期
	Persist(key string) (status bool, err error)
	// 将当前数据库的 key 移动到给定的数据库 db 当中
	Move(key string, db int) (status bool, err error)
	// 从当前数据库中随机返回一个 key
	RandomKey() (key string, err error)
	// 序列化给定 key，并返回被序列化的值
	Dump(key string) (value string, err error)
	// 反序列化给定的序列化值，并将它和给定的 key 关联，以毫秒为单位为 key 设置生存时间，如果 ttl 为 0 ，那么不设置生存时间
	Restore(key string, ttl int64, value string) (status string, err error)
	// 以秒为单位返回 key 的剩余过期时间
	TTL(key string) (time time.Duration, err error)
	// 设置 key 在 second 秒后过期，key 过期后将不再可用
	Expire(key string, second int64) (status bool, err error)
	// 用于删除已存在的键，不存在的 key 会被忽略
	Del(keys ...string) (count int64, err error)
	// 以毫秒为单位返回 key 的剩余过期时间
	PTTL(key string) (t time.Duration, err error)
	// 在新的 key 不存在时修改指定 key 的名称
	RenameNX(key string, newKey string) (status bool, err error)
	// 检查给定的多个 key 是否存在，返回存在的数量
	Exists(keys ...string) (count int64, err error)
	// 以指定 UNIX 时间戳格式设置 key 的过期时间，key 过期后将不再可用
	ExpireAt(key string, timestamp int64) (status bool, err error)
	// 用于查找所有符合给定模式 pattern 的 key
	Keys(pattern string) (keys []string, err error)
	// 迭代获取 Redis 键值，找出满足特定前缀 key 的列表
	Scan(cursor uint64, match string, count int64) (keys []string, nextCursor uint64, err error)
	// ------------------------ 字符串类型方法
	// 设置指定 key 的值，设置指定秒单位时间过期，0 则为永不过期，成功返回 "OK"
	Set(key string, value string, second int64) (status string, err error)
	// 只有在 key 不存在时设置 key 的值，设置指定秒单位时间过期，0 则为永不过期
	SetNX(key string, value string, second int64) (status bool, err error)
	// 获取指定 key 的值
	Get(key string) (value string, err error)
	// 获取并删除指定 key 的值
	GetWithDel(key string) (value string, err error)
	// 返回 key 中字符串值的子字符
	GetRange(key string, start int64, end int64) (child string, err error)
	// 将给定 key 的值设为 value ，并返回 key 的旧值(old value)
	GetSet(key string, value string) (old string, err error)
	// 对 key 所储存的字符串值，设置或清除指定偏移量上的位(bit)
	SetBit(key string, offset int64, value int) (count int64, err error)
	// 对 key 所储存的字符串值，获取指定偏移量上的位(bit)
	GetBit(key string, offset int64) (bit int64, err error)
	// 获取所有(一个或多个)给定 key 的值
	MGet(keys ...string) (values []interface{}, err error)
	// 用 value 参数覆写给定 key 所储存的字符串值，从偏移量 offset 开始
	SetRange(key string, offset int64, value string) (len int64, err error)
	// 返回 key 所储存的字符串值的长度
	StrLen(key string) (len int64, err error)
	// 同时设置一个或多个 key-value 对
	MSet(keyValues ...interface{}) (status string, err error)
	// 同时设置一个或多个 key-value 对，当且仅当所有给定 key 都不存在
	MSetNX(keyValues ...interface{}) (status bool, err error)
	// 将 key 中储存的数字值增一，返回处理结果值
	Incr(key string) (value int64, err error)
	// 将 key 所储存的值加上给定的增量值，返回处理结果值
	IncrBy(key string, incr int64) (value int64, err error)
	// 将 key 所储存的值加上给定的浮点增量值，返回处理结果值
	IncrByFloat(key string, incr float64) (value float64, err error)
	// 将 key 中储存的数字值减一，返回处理结果值
	Decr(key string) (value int64, err error)
	// key 所储存的值减去给定的减量值，返回处理结果值
	DecrBy(key string, decr int64) (value int64, err error)
	// 如果 key 不存在，则添加键值；如果已经存在并且是一个字符串， APPEND 命令将指定的 value 追加到该 key 原来值（value）的末，返回结果字符串长度
	Append(key string, value string) (len int64, err error)
	// ------------------------ Hash 类型操作
	// 删除一个或多个哈希表字段
	HDel(key string, fields ...string) (count int64, err error)
	// 查看哈希表 key 中，指定的字段是否存在
	HExists(key string, field string) (has bool, err error)
	// 获取存储在哈希表中指定字段的值
	HGet(key string, field string) (value string, err error)
	// 获取在哈希表中指定 key 的所有字段和值
	HGetAll(key string) (fieldValue map[string]string, err error)
	// 为哈希表 key 中的指定字段的整数值加上增量 increment，返回结果值
	HIncrBy(key string, field string, incr int64) (value int64, err error)
	// 为哈希表 key 中的指定字段的浮点数值加上增量 increment
	HIncrByFloat(key string, field string, incr float64) (value float64, err error)
	// 获取哈希表中所有的字段
	HKeys(key string) (fields []string, err error)
	// 获取哈希表中字段的数量
	HLen(key string) (len int64, err error)
	// 获取所有给定字段的值
	HMGet(key string, fields ...string) (values []interface{}, err error)
	// 同时将多个 field-value (域-值)对设置到哈希表 key 中
	HMSet(key string, fieldValues ...interface{}) (status bool, err error)
	// 将哈希表 key 中的字段 field 的值设为 value
	HSet(key string, field string, value string) (len int64, err error)
	// 只有在字段 field 不存在时，设置哈希表字段的值
	HSetNX(key string, field string, value string) (status bool, err error)
	// 获取哈希表中所有值
	HVals(key string) (values []string, err error)
	// 迭代获取哈希表中的键值对
	HScan(key string, cursor uint64, match string, count int64) (fieldValues []string, nextCursor uint64, err error)
	// ------------------------ List 列表类型操作
	// 移出并获取列表的第一个元素， 如果列表没有元素会阻塞列表直到等待指定秒单位时间超时或发现可弹出元素为止
	BLPop(timeout int64, key string) (value string, err error)
	// 移出并获取列表的最后一个元素， 如果列表没有元素会阻塞列表直到等待指定秒单位时间超时或发现可弹出元素为止
	BRPop(timeout int64, key string) (value string, err error)
	// 从列表中弹出一个值，将弹出的元素插入到另外一个列表中并返回它； 如果列表没有元素会阻塞列表直到等待指定秒单位时间超时或发现可弹出元素为止
	BRPopLPush(source, destination string, timeout int64) (value string, err error)
	// 通过索引获取列表中的元素
	LIndex(key string, index int64) (value string, err error)
	// 在指定列表的元素前插入元素，返回插入元素索引值
	LInsertBefore(key string, pivot, value interface{}) (index int64, err error)
	// 在指定列表的元素后插入元素，返回插入元素索引值
	LInsertAfter(key, pivot, value string) (index int64, err error)
	// 获取列表长度
	LLen(key string) (len int64, err error)
	// 移出并获取列表的第一个元素，返回值为移除的元素
	LPop(key string) (value string, err error)
	// 将一个或多个值插入到列表头部
	LPush(key string, values ...interface{}) (count int64, err error)
	// 将一个或多个值插入到已存在的列表头部
	LPushX(key string, values ...interface{}) (count int64, err error)
	// 获取列表指定索引范围内的元素
	LRange(key string, start, end int64) (values []string, err error)
	// 移除列表元素，根据参数 COUNT 的值，移除列表中与参数 VALUE 相等的元素
	LRem(key string, count int64, value string) (index int64, err error)
	// 通过索引设置列表元素的值
	LSet(key string, index int64, value string) (status string, err error)
	// 对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删
	LTrim(key string, start, end int64) (status string, err error)
	// 移除列表的最后一个元素，返回值为移除的元素
	RPop(key string) (value string, err error)
	// 移除列表的最后一个元素，并将该元素添加到另一个列表并返回
	RPopLPush(source, destination string) (value string, err error)
	// 将一个或多个值插入到列表尾部
	RPush(key string, values ...interface{}) (index int64, err error)
	// 将一个或多个值插入到已存在的列表尾部
	RPushX(key string, values ...interface{}) (index int64, err error)
	// ------------------------ Set 集合相关操作
	// 向集合添加一个或多个成员
	SAdd(key string, members ...interface{}) (count int64, err error)
	// 获取集合的成员数
	SCard(key string) (count int64, err error)
	// 返回第一个集合与其他集合之间的差异，切片第一个值为第一个集合键值
	SDiff(keys ...string) (members []string, err error)
	// 返回给定所有集合的差集并存储在 destination 中
	SDiffStore(destination string, keys ...string) (count int64, err error)
	// 返回给定所有集合的交集
	SInter(keys ...string) (members []string, err error)
	// 返回给定所有集合的交集并存储在 destination 中
	SInterStore(destination string, keys ...string) (count int64, err error)
	// 判断 member 元素是否是集合 key 的成员
	SIsMember(key string, member string) (status bool, err error)
	// 返回集合中的所有成员
	SMembers(key string) (members []string, err error)
	// 将 member 元素从 source 集合移动到 destination 集合
	SMove(source string, destination string, member string) (status bool, err error)
	// 随机移除并返回集合中的一个元素
	SPop(key string) (member string, err error)
	// 随机返回集合中一个成员
	SRandMember(key string) (member string, err error)
	// 移除集合中一个或多个成员
	SRem(key string, members ...string) (count int64, err error)
	// 返回所有给定集合的并集
	SUnion(keys ...string) (members []string, err error)
	// 所有给定集合的并集存储在 destination 集合中
	SUnionStore(destination string, keys ...string) (count int64, err error)
	// 迭代获取集合中的元素
	SScan(key string, cursor uint64, match string, count int64) (members []string, nextCursor uint64, err error)
	// ------------------------ ZSet 有序集合相关操作
	// 向有序集合添加（更新）一个或多个成员
	ZAdd(key string, scoreMembers ...*redis.Z) (count int64, err error)
	// 获取有序集合的成员的数量
	ZCard(key string) (count int64, err error)
	// 计算在有序集合中指定区间分数的成员数
	ZCount(key string, start string, end string) (count int64, err error)
	// 有序集合中对指定成员的分数加上增量 increment
	ZIncrBy(key string, incr float64, member string) (score float64, err error)
	// 计算给定的一个或多个有序集的交集，并将结果集存储在新的有序集合 key 中
	ZInterStore(destination string, opt *redis.ZStore) (count int64, err error)
	// 在有序集合中计算指定字典区间内成员数量
	ZLexCount(key, min, max string) (count int64, err error)
	// 通过索引区间返回有序集合指定区间内的成员
	ZRange(key string, start, end int64) (members []string, err error)
	// 通过字典区间返回有序集合的成员
	ZRangeByLex(key string, opt *redis.ZRangeBy) (members []string, err error)
	// 通过分数返回有序集合指定区间内的成员
	ZRangeByScore(key string, opt *redis.ZRangeBy) (members []string, err error)
	// 返回有序集合中指定成员的索引
	ZRank(key string, member string) (index int64, err error)
	// 移除有序集合中的一个或多个成员
	ZRem(key string, members ...interface{}) (count int64, err error)
	// 移除有序集合中给定的字典区间的所有成员
	ZRemRangeByLex(key, min string, max string) (count int64, err error)
	// 移除有序集合中给定索引区间的所有成员
	ZRemRangeByRank(key string, start, end int64) (count int64, err error)
	// 移除有序集合中给定的分数区间的所有成员，包含边界值
	ZRemRangeByScore(key string, start string, end string) (count int64, err error)
	// 返回有序集中指定索引区间内的成员，分数从高到低
	ZRevRange(key string, start, end int64) (members []string, err error)
	// 返回有序集中指定分数区间内的成员，分数从高到低排序
	ZRevRangeByScore(key string, opt *redis.ZRangeBy) (members []string, err error)
	// 返回有序集合中指定成员的排名，有序集成员按分数值递减(从大到小)排序
	ZRevRank(key string, member string) (rank int64, err error)
	// 返回有序集中，成员的分数值
	ZScore(key string, member string) (score float64, err error)
	// 计算给定的一个或多个有序集的并集，并存储在新的 key 中
	ZUnionStore(destination string, opt *redis.ZStore) (count int64, err error)
	//迭代有序集合中的元素（包括元素成员和元素分值）
	ZScan(key string, cursor uint64, match string, count int64) (scoreMember []string, nextCursor uint64, err error)
	// ------------------------ Geo 类型操作
	// 将给定的空间元素（纬度、经度、名字）添加到指定的键里面，返回新添加到键里面的空间元素数量， 不包括那些已经存在但是被更新的元素
	GeoAdd(key string, geoLocations ...*redis.GeoLocation) (count int64, err error)
	// 从键里面返回所有给定位置元素的位置（经度和纬度）,返回一个数组， 数组中的每个项都由两个元素组成，第一个是精度，第二个是纬度
	GeoPos(key string, members ...string) (geoPos []*redis.GeoPos, err error)
	// 返回两个给定位置之间的距离，如果两个位置之间的其中一个不存在， 那么命令返回空值
	// unit 取值范围：m-米/km-千米/mi-英里/ft-英尺
	GeoDist(key string, member1, member2 string, unit string) (dist float64, err error)
	// 以给定的经纬度为中心，查询在指定范围内的所有元素并返回
	GeoRadius(key string, longitude, latitude float64, query *redis.GeoRadiusQuery) (result []redis.GeoLocation, err error)
	// 以指定元素为中点，找出在指定范围内的所有元素并返回
	GeoRadiusByMember(key, member string, query *redis.GeoRadiusQuery) (result []redis.GeoLocation, err error)
	// 以给定的经纬度为中心，查询在指定范围内的所有元素，存储在指定的键值中，且返回元素数量
	// GeoRadiusQuery 结构中，Store 和 StoreDist 必填其一
	GeoRadiusStore(key string, longitude, latitude float64, query *redis.GeoRadiusQuery) (count int64, err error)
	// 以指定元素为中点，找出在指定范围内的所有元素，存储在指定的键值中，且返回元素数量
	// GeoRadiusQuery 结构中，Store 和 StoreDist 必填其一
	GeoRadiusByMemberStore(key, member string, query *redis.GeoRadiusQuery) (count int64, err error)
	// 返回一个或多个位置元素的 Geohash 表示
	GeoHash(key string, members ...string) (hashes []string, err error)
	// ------------------------ HyperLogLog 类型操作
	// 将任意数量的元素添加到指定的 HyperLogLog 里面
	PFAdd(key string, els ...interface{}) (count int64, err error)
	// 单个键时， 返回储存在给定键的 HyperLogLog 的近似基数， 如果键不存在， 那么返回 0;多个键时，返回所有给定 HyperLogLog 的并集的近似基数
	PFCount(keys ...string) (count int64, err error)
	// 将多个 HyperLogLog 合并（merge）为一个 HyperLogLog ， 合并后的 HyperLogLog 的基数接近于所有输入 HyperLogLog 的可见集合（observed set）的并集
	PFMerge(dest string, keys ...string) (status string, err error)
	// ------------------------ redis 数据库相关操作
	// 返回最近一次 Redis 成功将数据保存到磁盘上的时间，以 UNIX 时间戳格式表示
	LastSave() (timestamp int64, err error)
	// 获取 redis 服务的配置参数
	ConfigGet(parameter string) (config []interface{}, err error)
	// 动态地调整 Redis 服务器的配置(configuration)而无须重启
	ConfigSet(parameter string, value string) (config string, err error)
	// 用于返回当前数据库的 key 的数量
	DBSize() (count int64, err error)
	// 返回当前服务器格式化时间
	Time() (t time.Time, err error)
	// 以一种易于理解和阅读的格式，返回关于 Redis 服务器的各种信息和统计数值
	Info(section ...string) (serverInfo string, err error)
}

// 实现 redis 操作助手
type Handler struct {
	Client *redis.Client
	Logger *zap.Logger
}

// 记录 redis 操作日志
func (h *Handler) Log(info string, args, reply interface{}, err error) {
	if h.Logger != nil {
		h.Logger.Debug(info, zap.Any("args", args), zap.Any("reply", reply), zap.Error(err))
	} else {
		fmt.Println(info, args, reply, err)
	}
}
